/************************************************************************

					s			Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							 script_give_skill.cpp

	$Header: /heroes4/script_give_skill.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "script_give_skill.h"

#include "streambuf_operators.h"
#include "enum_operations.h"
#include "skill_properties.h"
#include "script_targeting.h"
#include "basic_dialog.h"

namespace {
	static std::list<t_skill>	g_skills_learned;
};

// --------------------------------------------------------------------------
// t_script_give_skill members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_script_give_skill::read_from_map(
	std::streambuf &	stream,
	int					format_version )
{
	if ( !t_script_hero_action::read_from_map( stream, format_version ) )
		return false;

	m_skill.skill = t_skill_type( get< t_uint8 >( stream ) );
	if ( m_skill.skill < 0 || m_skill.skill >= k_skill_type_count )
		return false;

	m_skill.level = t_skill_mastery( get< t_uint8 >( stream ) );
	return m_skill.level >= 0 && m_skill.level < k_skill_mastery_count;
}

bool t_script_give_skill::read( std::streambuf& stream, int version )
{
	if ( !t_script_hero_action::read( stream, version ) )
		return false;

	m_skill.skill = get< t_skill_type >( stream );
	if ( m_skill.skill < 0 || m_skill.skill >= k_skill_type_count )
		return false;

	m_skill.level = get< t_skill_mastery >( stream );
	return m_skill.level >= 0 && m_skill.level < k_skill_mastery_count;
}

bool t_script_give_skill::write( std::streambuf& stream ) const
{
	if ( !t_script_hero_action::write( stream ) )
		return false;

	put< t_skill_type >( stream, m_skill.skill );
	put< t_skill_mastery >( stream, m_skill.level );

	return true;
}

void t_script_give_skill::add_icons(t_basic_dialog* dialog) const 
{
	while (!g_skills_learned.empty()) {
		dialog->add_skill(g_skills_learned.back());
		g_skills_learned.pop_back();
	}
}

void t_script_give_skill::do_action(t_hero* hero) const
{
	if (hero == NULL) return;

	g_skills_learned.clear();

	// Ensure they won't exceed their primary skill limit.
	t_skill_type type = get_primary_skill(get_skill().skill);
	if ((hero->get_skill(type) == k_mastery_none) &&
		(hero->get_primary_skill_count() >= k_primary_skill_limit))
		return;

	// Step thru the requirements list adding skills where 
	// necessary.  Retain learned skills for add_icons
	std::vector<t_skill>::const_iterator index;
	std::list<t_skill> skill_stack;

	skill_stack.push_back( get_skill() );
	while (!skill_stack.empty())
	{
		t_skill skill = skill_stack.back();
		skill_stack.pop_back();
		if (hero->get_skill( skill.skill ) >= skill.level)
			continue;

		std::vector<t_skill> const&	requirements = get_requirements( skill );

		hero->learn_skill( skill );
		g_skills_learned.push_back(skill);
		for (index = requirements.begin(); index != requirements.end(); index++)
		{
			if (hero->get_skill( index->skill ) < index->level)
				skill_stack.push_back( *index );
		}
	}
}

// --------------------------------------------------------------------------
// t_script_can_give_skill members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_script_can_give_skill::t_result
t_script_can_give_skill::evaluate(t_expression_context_hero const& context) const
{
	t_hero const* hero = select_hero_by_target(get_target(), NULL, context.hero, context.army, context.opposing_army);
	return do_evaluate(hero);
}

t_script_can_give_skill::t_result
t_script_can_give_skill::evaluate(t_expression_context_town const& context) const
{
	t_hero const* hero = select_hero_by_target(get_target(), context.garrison, NULL, NULL, context.opposing_army);
	return do_evaluate(hero);
}

t_script_can_give_skill::t_result
t_script_can_give_skill::evaluate(t_expression_context_object const& context) const
{
	assert(false);
	return false;
}

t_script_can_give_skill::t_result
t_script_can_give_skill::evaluate(t_expression_context_army const& context) const
{
	t_hero const* hero = select_hero_by_target(get_target(), NULL, NULL, context.army, context.opposing_army);
	return do_evaluate(hero);
}

t_script_can_give_skill::t_result
t_script_can_give_skill::evaluate(t_expression_context_global const& context) const
{
	assert(false);
	return false;
}

t_script_can_give_skill::t_result
t_script_can_give_skill::do_evaluate(t_hero const* hero) const
{
	if (hero == false) return false;

	return hero->can_learn(get_skill());
}


// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_script_can_give_skill::read_from_map(
	std::streambuf &	stream,
	int					format_version )
{
	if ( !t_script_targeted_boolean_expression< t_script_hero_target >::read_from_map( stream, format_version ) )
		return false;

	m_skill.skill = t_skill_type( get< t_uint8 >( stream ) );
	if ( m_skill.skill < 0 || m_skill.skill >= k_skill_type_count )
		return false;

	m_skill.level = t_skill_mastery( get< t_uint8 >( stream ) );
	return m_skill.level >= 0 && m_skill.level < k_skill_mastery_count;
}

bool t_script_can_give_skill::read( std::streambuf& stream, int version )
{
	if ( !t_script_targeted_boolean_expression< t_script_hero_target >::read( stream, version ) )
		return false;

	m_skill.skill = get< t_skill_type >( stream );
	if ( m_skill.skill < 0 || m_skill.skill >= k_skill_type_count )
		return false;

	m_skill.level = get< t_skill_mastery >( stream );
	return m_skill.level >= 0 && m_skill.level < k_skill_mastery_count;
}

bool t_script_can_give_skill::write( std::streambuf& stream ) const
{
	if ( !t_script_targeted_boolean_expression< t_script_hero_target >::write( stream ) )
		return false;

	put< t_skill_type >( stream, m_skill.skill );
	put< t_skill_mastery >( stream, m_skill.level );

	return true;
}

// --------------------------------------------------------------------------
// t_script_increase_skill members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_script_increase_skill::read_from_map(
	std::streambuf &	stream,
	int					format_version )
{
	if ( !t_script_hero_adjustment_action< t_uint8 >::read_from_map( stream, format_version ) )
		return false;

	m_skill_type = t_skill_type( get< t_uint8 >( stream ) );
	return m_skill_type >= 0 && m_skill_type < k_skill_type_count;
}

bool t_script_increase_skill::read( std::streambuf& stream, int version )
{
	if ( !t_script_hero_adjustment_action< t_uint8 >::read( stream, version ) )
		return false;

	m_skill_type = get< t_skill_type >( stream );
	return m_skill_type >= 0 && m_skill_type < k_skill_type_count;
}

bool t_script_increase_skill::write( std::streambuf& stream ) const
{
	if ( !t_script_hero_adjustment_action< t_uint8 >::write( stream ) )
		return false;

	put< t_skill_type >( stream, m_skill_type );
	return true;
}

void t_script_increase_skill::add_icons(t_basic_dialog* dialog) const
{
	t_skill skill;
	while (!g_skills_learned.empty()) {
		skill = g_skills_learned.back();
		g_skills_learned.pop_back();
		dialog->add_skill(skill);
	}
}

void t_script_increase_skill::recursive_increase_skill(t_hero* hero, t_skill_type type) const
{
	if (hero == NULL) return;

	t_skill_mastery target_mastery = hero->get_skill(type);
	enum_incr(target_mastery);
	t_skill target_skill(type, target_mastery);

	std::vector<t_skill> const&	requirements = get_requirements( target_skill );
	std::vector<t_skill>::const_iterator index;
	for (index = requirements.begin(); index != requirements.end(); index++)
	{
		if (hero->get_skill( index->skill ) < index->level) {
			recursive_increase_skill(hero, index->skill);
			return;
		}
	}

	hero->learn_skill(target_skill);

	std::list<t_skill>::iterator list_index = g_skills_learned.begin();
	if (list_index->skill == type) {
		list_index->level = target_mastery;
		return;
	}
	g_skills_learned.push_front(target_skill);
}

void t_script_increase_skill::make_adjustment(t_adventure_map* map, t_hero* hero) const
{
	g_skills_learned.clear();

	// Ensure they won't exceed their primary skill limit.
	t_skill_type type = get_primary_skill(get_skill_type());
	if ((hero->get_skill(type) == k_mastery_none) &&
		(hero->get_primary_skill_count() >= k_primary_skill_limit))
		return;
	
	int adjustment_left = get_adjustment();
	while ((hero->get_skill(get_skill_type()) < k_mastery_grand_master)	&& adjustment_left--) {
		recursive_increase_skill(hero, get_skill_type());
	}
}

